﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CreateNewReportViewModel.cs" company="Spenta Consulting">
//   (c) Copyright 2011
// </copyright>
// <summary>
//   Create new report view model
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Spenta.StreetCare.Silverlight.MVVM.ViewModels
{
    #region Using Directives

    using System;
    using System.Collections.ObjectModel;
    using System.ComponentModel.DataAnnotations;
    using System.ServiceModel.DomainServices.Client;
    using System.Windows;
    using System.Windows.Browser;
    using System.Windows.Input;
    using GalaSoft.MvvmLight.Command;
    using GalaSoft.MvvmLight.Messaging;
    using Logging;
    using Messages;
    using Microsoft.Maps.MapControl;

    using Spenta.Silverlight.GeocodeService;
    using Spenta.StreetCare.Silverlight.MVVM.Validations;

    using StreetCareAPI;

    #endregion

    /// <summary>
    /// Create new report view model
    /// </summary>
    public class CreateNewReportViewModel : BaseViewModel
    {
        #region Private members

        /// <summary>
        /// Comment first validation scope
        /// </summary>
        private readonly ValidationScope reportFirstValidationScope = new ValidationScope();

        /// <summary>
        /// Comment second validation scope
        /// </summary>
        private readonly ValidationScope reportSecondValidationScope = new ValidationScope();

        /// <summary>
        /// Comment third validation scope
        /// </summary>
        private readonly ValidationScope reportThirdValidationScope = new ValidationScope();
        
        /// <summary>
        /// Private store for the report
        /// </summary>
        private Core.Models.Report report;

        /// <summary>
        /// Private store for the map clicked property
        /// </summary>
        private Visibility mapClicked = Visibility.Collapsed;

        /// <summary>
        /// Private store for the user
        /// </summary>
        private User user;

        /// <summary>
        /// Private store for the customer
        /// </summary>
        private Customer customer;

        /// <summary>
        /// Private store for the waiting visible property
        /// </summary>
        private bool waitingVisible;

        /// <summary>
        /// Private store for the second section visible property
        /// </summary>
        private bool secondSectionVisible;

        /// <summary>
        /// Private store for the third section visible property
        /// </summary>
        private bool thirdSectionVisible;

        /// <summary>
        /// Private store for the report validation errors
        /// </summary>
        private ObservableCollection<ValidationResult> reportValidationErrors = new ObservableCollection<ValidationResult>();

        /// <summary>
        /// Private store for selected section
        /// </summary>
        private int selectedSection;

        /// <summary>
        /// Private store for address
        /// </summary>
        private string address;

        /// <summary>
        /// Private store for Waiting locate address visible
        /// </summary>
        private bool waitingLocateAddressVisible;

        #endregion

        #region Class Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="CreateNewReportViewModel"/> class.
        /// </summary>
        public CreateNewReportViewModel()
        {
            if (HtmlPage.IsEnabled)
            {
                Messenger.Default.Register<Category>(this, this.SetCategory);
                Messenger.Default.Register<Customer>(this, customer => Customer = customer);
                Messenger.Default.Register<User>(this, user => User = user);
                Messenger.Default.Register<NewReportMessage>(this, this.InitReport);
                GeocodeClient.GeocodeCompleted += this.GeocodeClientGeocodeCompleted;
            }
        }

        #endregion

        #region ViewModel Properties

        /// <summary>
        /// Gets or sets the view map.
        /// </summary>
        /// <value>The view map.</value>
        public Map ViewMap { get; set; }

        /// <summary>
        /// Gets or sets the report.
        /// </summary>
        /// <value>The report.</value>
        public Core.Models.Report Report
        {
            get
            {
                return this.report;
            }

            set
            {
                this.report = value;
                RaisePropertyChanged("Report");
            }
        }

        /// <summary>
        /// Gets or sets the map clicked.
        /// </summary>
        /// <value>The map clicked.</value>
        public Visibility MapClicked
        {
            get
            {
                return this.mapClicked;
            }

            set
            {
                this.mapClicked = value;
                RaisePropertyChanged("MapClicked");
            }
        }

        /// <summary>
        /// Gets or sets the user.
        /// </summary>
        /// <value>
        /// The authenticated user.
        /// </value>
        public User User
        {
            get
            {
                return this.user;
            }

            set
            {
                this.user = value;
                RaisePropertyChanged("User");
            }
        }

        /// <summary>
        /// Gets or sets the customer.
        /// </summary>
        /// <value>
        /// The customer.
        /// </value>
        public Customer Customer
        {
            get
            {
                return this.customer;
            }

            set
            {
                this.customer = value; 
                this.ViewMap.SetView(this.customer.CustomerMapLocation, this.customer.CustomerZoom);
                RaisePropertyChanged("Customer");
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [waiting visible].
        /// </summary>
        /// <value><c>true</c> if [waiting visible]; otherwise, <c>false</c>.</value>
        public bool WaitingVisible
        {
            get
            {
                return this.waitingVisible;
            }

            set
            {
                this.waitingVisible = value;
                RaisePropertyChanged("WaitingVisible");
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [waiting locate address visible].
        /// </summary>
        /// <value><c>true</c> if [waiting locate address visible]; otherwise, <c>false</c>.</value>
        public bool WaitingLocateAddressVisible
        {
            get
            {
                return this.waitingLocateAddressVisible;
            }

            set
            {
                this.waitingLocateAddressVisible = value;
                RaisePropertyChanged("WaitingLocateAddressVisible");
            }
        }

        /// <summary>
        /// Gets or sets the selected section.
        /// </summary>
        /// <value>The selected section.</value>
        public int SelectedSection
        {
            get
            {
                return this.selectedSection;
            }

            set
            {
                this.selectedSection = value;
                this.RaisePropertyChanged("SelectedSection");
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [second section visible].
        /// </summary>
        /// <value><c>true</c> if [second section visible]; otherwise, <c>false</c>.</value>
        public bool SecondSectionVisible
        {
            get
            {
                return this.secondSectionVisible;
            }

            set
            {
                this.secondSectionVisible = value;
                RaisePropertyChanged("SecondSectionVisible");
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [third section visible].
        /// </summary>
        /// <value><c>true</c> if [third section visible]; otherwise, <c>false</c>.</value>
        public bool ThirdSectionVisible
        {
            get
            {
                return this.thirdSectionVisible;
            }

            set
            {
                this.thirdSectionVisible = value;
                RaisePropertyChanged("ThirdSectionVisible");
            }
        }

        /// <summary>
        /// Gets or sets the report validation errors.
        /// </summary>
        /// <value>The report validation errors.</value>
        public ObservableCollection<ValidationResult> ReportValidationErrors
        {
            get
            {
                return this.reportValidationErrors;
            }

            set
            {
                this.reportValidationErrors = value;
                this.RaisePropertyChanged("ReportValidationErrors");
            }
        }

        /// <summary>
        /// Gets the report first validation scope.
        /// </summary>
        /// <value>The report first validation scope.</value>
        public ValidationScope ReportFirstValidationScope
        {
            get
            {
                return this.reportFirstValidationScope;
            }
        }

        /// <summary>
        /// Gets the report second validation scope.
        /// </summary>
        /// <value>The report second validation scope.</value>
        public ValidationScope ReportSecondValidationScope
        {
            get
            {
                return this.reportSecondValidationScope;
            }
        }

        /// <summary>
        /// Gets the report third validation scope.
        /// </summary>
        /// <value>The report third validation scope.</value>
        public ValidationScope ReportThirdValidationScope
        {
            get
            {
                return this.reportThirdValidationScope;
            }
        }

        /// <summary>
        /// Gets or sets the address.
        /// </summary>
        /// <value>The address.</value>
        public string Address
        {
            get
            {
                return this.address;
            }

            set
            {
                this.address = value;
                this.RaisePropertyChanged("Address");
            }
        }

        /// <summary>
        /// Gets the map click command reference.
        /// </summary>
        /// <value>
        /// The map click command.
        /// </value>
        public ICommand MapClickCommand
        {
            get
            {
                return new RelayCommand<MapMouseEventArgs>(this.MapClick);
            }
        }

        /// <summary>
        /// Gets the create report command reference.
        /// </summary>
        /// <value>
        /// The create report command.
        /// </value>
        public ICommand CreateReportCommand
        {
            get
            {
                return new RelayCommand(this.CreateReport);
            }
        }

        /// <summary>
        /// Gets the first section next command reference.
        /// </summary>
        /// <value>
        /// The first section nex command.
        /// </value>
        public ICommand FirstSectionNextCommand
        {
            get
            {
                return new RelayCommand(this.FirstSectionNext);
            }
        }

        /// <summary>
        /// Gets the second section next command reference.
        /// </summary>
        /// <value>
        /// The second section nex command.
        /// </value>
        public ICommand SecondSectionNextCommand
        {
            get
            {
                return new RelayCommand(this.SecondSectionNext);
            }
        }

        /// <summary>
        /// Gets the locate address command.
        /// </summary>
        /// <value>The locate address command.</value>
        public ICommand LocateAddressCommand
        {
            get
            {
                return new RelayCommand(this.LocateAddress);
            }
        }

        #endregion

        #region Commands

        /// <summary>
        /// Inits the report.
        /// </summary>
        /// <param name="message">The message.</param>
        public void InitReport(NewReportMessage message)
        {
            this.SelectedSection = 0;
            this.SecondSectionVisible = false;
            this.ThirdSectionVisible = false;

            this.Report = new Core.Models.Report
            {
                ReportNotificationTypeID = 1,
                ReportWhen = DateTime.Now,
                ReportImageUrl = "/images/default/nopicture.png"
            };    
        }

        /// <summary>
        /// Sets the category.
        /// </summary>
        /// <param name="category">The category.</param>
        public void SetCategory(Category category)
        {
            this.Report.Category = new Core.Models.Category
                {
                    CategoryID = category.CategoryID,
                    CategoryTitle = category.CategoryTitle,
                    CategoryCustomerID = category.CategoryCustomerID,
                    CategoryDescription = category.CategoryDescription,
                    CategoryImageUrl = category.CategoryImageUrl,
                    CategoryKey = category.CategoryKey,
                    CategoryColor = category.CategoryColor
                };
        }

        /// <summary>
        /// Handles the map click command: moves to the next map
        /// </summary>
        /// <param name="e">The <see cref="Microsoft.Maps.MapControl.MapMouseEventArgs"/> instance containing the event data.</param>
        public void MapClick(MapMouseEventArgs e)
        {
            Location location;
            if (this.ViewMap.TryViewportPointToLocation(e.ViewportPoint, out location))
            {
                this.MapClicked = Visibility.Visible;
                this.Report.ReportLatitude = location.Latitude;
                this.Report.ReportLongitude = location.Longitude;

                RaisePropertyChanged("Report");
            }
        }

        /// <summary>
        /// Handles the first section next command.
        /// </summary>
        public void FirstSectionNext()
        {
            this.ReportFirstValidationScope.ValidateScope();
            if (!this.ReportFirstValidationScope.IsValid())
            {
                return;
            }

            this.SecondSectionVisible = true;
            this.SelectedSection = 1;
        }

        /// <summary>
        /// Handles the second section next command.
        /// </summary>
        public void SecondSectionNext()
        {
            this.ReportSecondValidationScope.ValidateScope();
            if (!this.ReportSecondValidationScope.IsValid())
            {
                return;
            }

            this.ThirdSectionVisible = true;
            this.SelectedSection = 2;
        }

        /// <summary>
        /// Handles the create report command.
        /// </summary>
        public void CreateReport()
        {
            this.ReportFirstValidationScope.ValidateScope();
            if (!this.ReportFirstValidationScope.IsValid())
            {
                this.SelectedSection = 0;
                return;
            }

            this.ReportSecondValidationScope.ValidateScope();
            if (!this.ReportSecondValidationScope.IsValid())
            {
                this.SelectedSection = 1;
                return;
            }

            this.ReportThirdValidationScope.ValidateScope();
            if (!this.ReportThirdValidationScope.IsValid())
            {
                this.SelectedSection = 2;
                return;
            }

            this.WaitingVisible = true;

            this.Report.ReportCustomerID = this.Customer.CustomerID;
            this.Report.ReportBy = this.User.UserID;

            // TODO: upload a real image
            this.Report.ReportImageUrl = "/images/default/nopicture.png";

            ApiContext.CreateReport(this.report, this.CreateReportCompleted, null);
        }

        /// <summary>
        /// Handles the locate address command.
        /// </summary>
        public void LocateAddress()
        {
            if (string.IsNullOrEmpty(this.Address))
            {
                return;
            }

            this.WaitingLocateAddressVisible = true;

            // Set the credentials and the geocode query, which could be an address or location.
            var geocodeRequest = new GeocodeRequest
            {
                Credentials =
                    new Credentials
                    {
                        ApplicationId =
                            ((ApplicationIdCredentialsProvider)this.ViewMap.CredentialsProvider).ApplicationId
                    },
                Query = this.Address
            };

            // Make the asynchronous Geocode request, using the ‘waypoint index’ as 
            //   the user state to track this request and allow it to be identified when the response is returned.
            GeocodeClient.GeocodeAsync(geocodeRequest);
        }

        #endregion

        #region Event Methods

        /// <summary>
        /// Handles the create report completed event.
        /// </summary>
        /// <param name="io">The invoke operation.</param>
        private void CreateReportCompleted(InvokeOperation<Core.Models.Report> io) 
        {
            if (io.HasError)
            {
                LogHelper.Logger.WriteError("Error creating report" + io.Error.Message);
                Messenger.Default.Send(new NewReportCompletedMessage { Exception = io.Error });
                return;
            }

            // TODO: Extend RIA Services
            Messenger.Default.Send(
                new NewReportCompletedMessage
                    {
                        Report =
                            new Report
                                {
                                    ReportBy = io.Value.ReportBy,
                                    ReportCategoryID = io.Value.ReportCategoryID,
                                    Category = new Category
                                        {
                                            CategoryID = this.Report.Category.CategoryID,
                                            CategoryKey = this.Report.Category.CategoryKey,
                                            CategoryImageUrl = this.Report.Category.CategoryImageUrl,
                                            CategoryDescription = this.Report.Category.CategoryDescription,
                                            CategoryTitle = this.Report.Category.CategoryTitle,
                                            CategoryColor = this.Report.Category.CategoryColor
                                        },
                                    ReportCustomerID = io.Value.ReportCustomerID,
                                    ReportFixedImageUrl = io.Value.ReportFixedImageUrl,
                                    ReportID = io.Value.ReportID,
                                    ReportImageUrl = io.Value.ReportImageUrl,
                                    ReportLatitude = io.Value.ReportLatitude,
                                    ReportLocation = io.Value.ReportLocation,
                                    ReportLongitude = io.Value.ReportLongitude,
                                    ReportNotificationDetails = io.Value.ReportNotificationDetails,
                                    ReportNotificationTypeID = io.Value.ReportNotificationTypeID,
                                    ReportStatusID = io.Value.ReportStatusID,
                                    ReportStatusWhen = io.Value.ReportStatusWhen,
                                    ReportTitle = io.Value.ReportTitle,
                                    ReportWhen = io.Value.ReportWhen,
                                    User = new User
                                    {
                                        UserID = this.User.UserID,
                                        UserIdentityProvider = this.User.UserIdentityProvider,
                                        UserImageUrl = this.User.UserImageUrl,
                                        UserNameIdentifier = this.User.UserNameIdentifier,
                                        UserNickname = this.User.UserNickname
                                    }
                                }
                    });
            this.WaitingVisible = false;
        }

        /// <summary>
        /// Handles the GeocodeCompleted event of the GeocodeClient control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Spenta.StreetCare.Silverlight.GeocodeService.GeocodeCompletedEventArgs"/> instance containing the event data.</param>
        private void GeocodeClientGeocodeCompleted(object sender, GeocodeService.GeocodeCompletedEventArgs e)
        {
            if (!e.Cancelled
                && e.Result.ResponseSummary.StatusCode == ResponseStatusCode.Success
                && e.Result.Results.Count > 0)
            {
                // Only report on first result.
                var geocodeLocation = e.Result.Results[0].Locations[0];
                var location = new Location(geocodeLocation.Latitude, geocodeLocation.Longitude, geocodeLocation.Altitude);

                // Zoom the map to the location of the item.
                this.ViewMap.SetView(location, 18);

                // Place pushpin and set report location
                this.MapClicked = Visibility.Visible;
                this.Report.ReportLatitude = location.Latitude;
                this.Report.ReportLongitude = location.Longitude;

                RaisePropertyChanged("Report");
            }

            this.WaitingLocateAddressVisible = false;
        }

        #endregion
    }
}
